// Copyright (c) 2008 Matthew Johnson
// Licensed under the MIT License (see license.txt)
package actionml.core {
	import actionml.Global;
	
	public class ArrayMethods {
		public static function initialize() {
			Global.initializeExtension(ArrayMethods, Array);
		}
		public static var instanceMethods:Object = {
			toString: function() {
				return "[" + this.join(", ") + "]";
			},
			equals: function(o:*) {
				if (o is Array && this.length == o.length) {
					for (var i:int = 0; i < this.length; i ++) {
						if (!this[i].equals(o[i])) {
							return false;
						}
					}
				} else {
					return false;
				}
				return true;
			},
			clone: function() {
				return this.slice(0, this.length);
			},
			evaluateExpression: function(context:Object=null) {
				var addSubtract:Array = [];
				for (var i:int = 0; i < this.length; i++) {
					if (this[i] == "*") {
						addSubtract.push(addSubtract.pop().evaluateExpression(context) * this[i + 1].evaluateExpression(context));
						i++
					} else if (this[i] == "/") {
						addSubtract.push(addSubtract.pop().evaluateExpression(context) / this[i + 1].evaluateExpression(context));
						i++
					} else {
						addSubtract.push(this[i]);
					}
				}
				var ands:Array = [];
				for (i = 0; i < addSubtract.length; i++) {
					if (addSubtract[i] == "+") {
						ands.push(ands.pop().evaluateExpression(context) + addSubtract[i + 1].evaluateExpression(context));
						i++
					} else if (addSubtract[i] == "-") {
						ands.push(ands.pop().evaluateExpression(context) - addSubtract[i + 1].evaluateExpression(context));
						i++
					} else {
						ands.push(addSubtract[i]);
					}
				}
				var ors:Array = [];
				for (i = 0; i < ands.length; i++) {
					if (ands[i] == "&&") {
						ors.push(ors.pop().evaluateExpression(context) && ands[i + 1].evaluateExpression(context));
						i++
					} else {
						ors.push(ands[i]);
					}
				}
				var result:Number = ors[0].evaluateExpression(context);
				for (i = 1; i < ors.length; i++) {
					if (ors[i] == "||") {
						result ||= ors[i + 1].evaluateExpression(context);
						i++
					}
				}
				if ("method" in this) {
					if (this.method in result && result[this.method] is Function) {
						return result[this.method]();
					} else {
						return result.evaluateMethod(this.method);
					}
				} else {
				return result;
				}
			},
			
			append: function(...items:*) {
				var flattenedItems:Array = items.flatten();
				for each (var item:* in flattenedItems) {
					this.push(item);
				}
				return this;
			},
			appendIfNotPresent: function(...items:*) {
				var flattenedItems:Array = items.flatten();
				for each (var item:* in flattenedItems) {
					if (!this.contains(item)) {
						this.push(item);
					}
				}
				return this;
			},
			prepend: function(...items:*) {
				items = items.flatten();
				for (var i:int = items.length - 1; i >= 0; i--) {
					this.unshift(items[i]);
				}
				return this;
			},
			prependIfNotPresent: function(...items:*) {
				items = items.flatten();
				for (var i:int = items.length - 1; i >= 0; i--) {
					if (!this.contains(items[i])) {
						this.unshift(items[i]);
					}
				}
				return this;
			},
			remove: function(...items:*) {
				items = items.flatten();
				for (var i:int = 0; i < this.length; i++) {
					for each (var item:* in items) {
						if (item === this[i]) {
							this.splice(i, 1)
						}
					}
				}
				return this;
			},
			
			flatten: function() {
				var r:Array = [];
				for each (var e:* in this) {
					if (e is Array) {
						for each (var e2:* in arguments.callee.call(e)) {
							r.push(e2);
						}
					} else {
						r.push(e);
					}
				}
				return r;
			},
			contains: function(item:*) {
				for each (var e:* in this) {
					if (item === e) {
						return true;
					}
				}
				return false;
			},
			
			simpleMap: function(f:Function) {
				return this.map(function(e:*, index:int, array:Array) { return f(e); });
			},
			simpleSome: function(f:Function) {
				return this.some(function(e:*, index:int, array:Array) { return f(e); });
			},
			
			random: function() {
				return this[Math.floor(Math.random() * this.length)];	
			},
			shuffle: function() {
				var shuffled:Array = new Array(this.length);
				for each (var o:Object in this) {
					var i:int = Math.floor(Math.random() * this.length);
					while (shuffled[i] != null && shuffled[i] != undefined) {
						i = Math.floor(Math.random() * this.length);
					}
					shuffled[i] = o;
				}
				return shuffled;
			}
		};
	}
}